using System;
using System.Collections;
using GeneralClasses;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;


// To create a Schedule_Population:
// 1. Create a new Schedule_Population and pass it a ScheduleData object
// 2. Call the doWork function of the Schedule_Population and pass it: 
//                                                                     initial population
//                                                                     the number of schedules at the end of the run
//                         
// 3. For example, if you want to create an initial population of 1000 and keep the best 10: 

//    Schedule_Population temp = new Schedule_Population(data);
//    List <Schedule> schedule_list= new List <Schedule>();
//    schedule_list = temp.doGenetic(1000, 10);

// The resulting schedules after the algorithm is completed will be contained in the List of schedules
// (pointed to by the pointer you created in Step 1).

namespace Algorithm
{

    public class Schedule_Population
    {
        public static Random TheSeed = new Random((int)DateTime.Now.Ticks);
        //ArrayList Schedules = new ArrayList();
        List<Single_Schedule> Schedules = new List<Single_Schedule>();
        ScheduleData data;


        #region CONSTRUCTOR
        public Schedule_Population(ScheduleData data)
        {
            this.data = data;


        }//Schedule_Population
        #endregion

        #region DO_GENETIC
        public List<Schedule> doGenetic(int pinit_pop, int min_schedules)
        {

            //BackgroundWorker worker = new BackgroundWorker();
            // worker.DoWork += DoWork;
            try
            {
                int generation = 1;
                int department_nurse_total = 0;
                for (int k = 0; k < data.Departments.Count; k++)
                {
                    department_nurse_total += data.GetNumNursesPerDepartment(k);

                }//for


                // Assuming all departments have the same number of shifts..
                //int plength = data.NumDays * data.Departments[0].Shifts.Count * department_nurse_total;
                int shifts = 3;
                int plength = data.NumDays * department_nurse_total * shifts;
                int num_nurses = data.Persons.Count;

                for (int z = 0; z < pinit_pop; z++)
                {
                    Single_Schedule aSingle_Schedule = new Single_Schedule(plength, 0, num_nurses, data);
                    Schedules.Add(aSingle_Schedule);
                }


                Schedules.Sort();
                int pop = pinit_pop / 2;

                while (pop >= min_schedules)
                {
                    Schedules = this.Mutate(Schedules, min_schedules);
                    IEnumerator Enum = Schedules.GetEnumerator();


                    while (Enum.MoveNext())
                    {
                        Single_Schedule cur = (Single_Schedule)Enum.Current;
                    }

                    if (Schedules.Count > min_schedules)
                    {
                        Schedules.Sort();
                        pop = pop / 2;
                        generation++;
                    }
                    else
                        break;
                }

                IEnumerator Enum2 = Schedules.GetEnumerator();
                Single_Schedule temp;
                List<Schedule> schedule_list = new List<Schedule>();

                for (int z = 0; z < Schedules.Count; z++)
                {
                    schedule_list.Add(Schedules[z].ToSchedule());
                }
                // while (Enum2.MoveNext())
                // {
                //     temp = (Single_Schedule) Enum2.Current;
                //    schedule_list.Add(temp.ToSchedule());
                // }

                return schedule_list;
            }
            catch (Exception e)
            {
                UTIL.sayDebug(e);
                Console.WriteLine("{0} Exception caught.", e);
                throw e;

            }

        }
        #endregion

        //void DoWork(object sender, DoWorkEventArgs e)
        //{
        // BackgroundWorker worker = sender as BackgroundWorker;

        // int population = (int) e.Argument;
        // List<Single_Schedule> schedules = new List<Single_Schedule>();
        // for(int i=0; i<population; i++)
        // {




        //}

        #region DO_RANDOM
        public List<Schedule> doRandom(int min_schedules)
        {
            try
            {
                int department_nurse_total = 0;
                for (int k = 0; k < data.Departments.Count; k++)
                {
                    department_nurse_total += data.GetNumNursesPerDepartment(k);
                }//for

                int plength = data.NumDays * 3 * department_nurse_total;
                int num_nurses = data.Persons.Count;

                List<Schedule> schedule_list = new List<Schedule>();

                for (int i = 0; i < min_schedules; i++)
                {

                    Single_Schedule aSingle_Schedule = new Single_Schedule(plength, 0, num_nurses, data);
                    schedule_list.Add(aSingle_Schedule.ToSchedule());
                    Schedules.Add(aSingle_Schedule);

                }

                //Schedules = this.Mutate(Schedules, 2);

                return schedule_list;
            }
            catch (Exception e)
            {
                // Console.WriteLine("{0} Exception caught.", e);
                UTIL.sayDebug(e);
                throw e;
            }

        }

        #endregion
        #region MUTATE
        public List<Single_Schedule> Mutate(List<Single_Schedule> schedules, int min_schedules)
        {
            try
            {

                int flag = 0;

                List<Single_Schedule> top = new List<Single_Schedule>(schedules.GetRange(0, min_schedules));
                schedules.RemoveRange(0, min_schedules);

                List<Single_Schedule> newList = new List<Single_Schedule>();
                int department_nurse_total = 0;

                for (int k = 0; k < data.Departments.Count; k++)
                {
                    department_nurse_total += data.Departments[k].PeopleNeeded;
                }//for

                int pivot = 0;
                int max = data.NumDays * department_nurse_total * 3;


                IEnumerator Enum = schedules.GetEnumerator();
                Enum.Reset();

                while (true)
                {
                    if (schedules.Count == 0)
                    {
                        flag = 1;
                        break;
                    }
                    else if (schedules.Count == 1)
                    {
                        schedules.RemoveAt(0);
                        flag = 1;
                        break;
                    }

                    //if (schedules.Count <= min_schedules) break;

                    if (flag != 1)
                    {
                        pivot = TheSeed.Next(0, max);
                        Single_Schedule prev = schedules[0];
                        Single_Schedule temp = schedules[1];
                        temp.SetArray(prev.Mutate(0, pivot), pivot);
                        Single_Schedule newElement = temp;
                        newList.Add(newElement);
                        schedules.RemoveRange(0, 2);
                    }
                }





                List<Single_Schedule> final_list = new List<Single_Schedule>();
                final_list.AddRange(top);
                final_list.AddRange(newList);
                flag = 0;
                return final_list;
            }
            catch (Exception e)
            {
                //Console.WriteLine("{0} Exception caught.", e);
                //return null;
                UTIL.sayDebug(e);
                throw e;
            }

        }//Mutate
        #endregion
    }
}
